package lab;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.hibernate.HibernateException;
import org.hibernate.service.UnknownUnwrapTypeException;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.service.spi.Configurable;
import org.hibernate.service.spi.Stoppable;

public class DataSourceConnectionProvider implements ConnectionProvider,
        Configurable, Stoppable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private Properties _props;
    private String dbName;
    private boolean available = true;

    private static Map<String, DataSource> dataSources;

    static {
        dataSources = new HashMap<String, DataSource>();
    }

    public DataSourceConnectionProvider(String dbName) {
        this.dbName = dbName;
    }

    @SuppressWarnings( { "unchecked" })
    public void configure(Map properties) {
        _props = new Properties();
        _props.putAll(properties);
    }

    public Connection getConnection() throws SQLException {
        if (!available) {
            throw new HibernateException("Provider is closed!");
        }
        DataSource dataSource = getDataSource(dbName, _props.getProperty("url")
                + ";user=" + _props.getProperty("userName") + ";password="
                + _props.getProperty("password"));

        return dataSource.getConnection();

        /*
         * Connection c = DriverManager.getConnection(_props.getProperty("url"),
         * _props.getProperty("userName"), _props.getProperty("password"));
         * return c;
         */
    }

    public void closeConnection(Connection con) throws SQLException {
        con.close();
    }

    public boolean supportsAggressiveRelease() {
        return true;
    }

    @SuppressWarnings("unchecked")
    public boolean isUnwrappableAs(Class unwrapType) {
        return ConnectionProvider.class.equals(unwrapType)
                || DataSourceConnectionProvider.class
                        .isAssignableFrom(unwrapType);
    }

    @SuppressWarnings("unchecked")
    public <T> T unwrap(Class<T> unwrapType) {
        if (ConnectionProvider.class.equals(unwrapType)
                || DataSourceConnectionProvider.class
                        .isAssignableFrom(unwrapType)) {
            return (T) this;
        } else {
            throw new UnknownUnwrapTypeException(unwrapType);
        }
    }

    public void stop() {
        available = false;
    }

    private static DataSource getDataSource(String key, String connectURI) {
        if (dataSources.get(key) == null) {
            synchronized (dataSources) {
                if (dataSources.get(key) == null) {
                    GenericObjectPool connectionPool = new GenericObjectPool(
                            null);
                    connectionPool.setMaxActive(5);
                    connectionPool
                            .setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);
                    connectionPool.setTimeBetweenEvictionRunsMillis(600000);
                    ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
                            connectURI, null);
                    new PoolableConnectionFactory(connectionFactory,
                            connectionPool, null, null, false, true);
                    PoolingDataSource dataSource = new PoolingDataSource(
                            connectionPool);
                    dataSources.put(key, dataSource);
                }
            }
        }

        return dataSources.get(key);
    }
}